﻿#define DEBUG

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

namespace UartAssistant
{
    public partial class MainForm : Form
    {
		// 与自动发送有关的全局变量
		public int autoSendNumber;
		public int currentSendNumber;
		public int autoSendPeriod;
		public List<byte> autoSendFrame;
		public string sendStringASCII;		// RichTextBox有个bug，"\v" "\n" "\r"不分，木办法出此下策

        // 功率等级数组,共16项
        const int RF_POWER_RANKGING = 16;
        private byte[] RF_power = new byte[RF_POWER_RANKGING]{
	        0x03,
	        0x06,
	        0x07,
	        0x0B,
	        0x0F,
	        0x13,
	        0x17,
	        0x1B,
	        0x1F,
	        0x3F,
	        0x5F,
	        0x7F,
	        0x9F,
	        0xBF,
	        0xDF,
	        0xFF
        };

        // 定义操作类型的常量
        const int FRAME_DELIMETER = 0X7E;       // packet delimeter
        const int FRAME_TYPE_TX_DATA = 0x10;	// send data from pc/arm to cc2430
        const int FRAME_TYPE_RX_DATA = 0x90;	// send data from 2430 to pc/arm
        const int FRAME_TYPE_SET_PARA = 0x08;	// set parameters from pc/arm
        const int FRAME_TYPE_GET_PARA = 0x09;	//  pc/arm is querying for para value
        const int FRAME_TYPE_RET_PARA = 0x8B;	// return parameters to pc/arm

        const int FRAME_TYPE_GET_STATISTIC = 0x70;	// pc/arm to cc2430
        const int FRAME_TYPE_RET_STATISTIC = 0x71;	// cc2430 return STATISTIC info to pc/arm
		const int FRAME_TYPE_CLR_STATISTIC = 0x72;	// pc/arm to clear the STATISTIC information
		const int PAPRA_TYPE_GET_CPSR = 0x73;		// Get Current Pointer Status.
		const int PAPRA_TYPE_RET_CPSR = 0x74;		// Ret Current Pointer Status.

        const int PARA_TYPE_TXPOWER = 0x01;
        const int PARA_TYPE_MYSADDR = 0x02;     // my short addr, 2 bytes
        const int PARA_TYPE_RSSI = 0x03;    	// received signal strength indicator
        const int PARA_TYPE_TEMPERATURE = 0x04;	// temperature

		const int PARA_TYPE_VERSION = 0x10;	// Stack version no.

		                            // 0x22, 0x00(MAC发送成功包), 
                            // 0x04, 0x00(MAC层接收包), 
                            // 0x00, 0x00(MAC Buff Full而放弃的包), 
                            // 0x00, 0x00(MAC发送错误),
                            // 0x00, 0x00(received data for self node), 
                            // 0x1E, 0x00(sent data from self), 
                            // 0x00, 0x00(data dropped due to send buffer full), 
        private string[] satisticsInformation = new string[] { 
            "MAC发送成功包: \t\t\t",
            "MAC层接收包: \t\t\t",
            "MAC缓冲区满丢弃的包: \t\t",
            "MAC发送失败: \t\t\t",
            "未定义: \t\t\t",
            "PC/ARM数据发送请求数: \t\t",
            "PC/ARM数据帧和命令帧总个数: \t"
        };

		private string[] CPSRInformation = new string[] { 
            "UART缓冲写指针: \t",
            "UART缓冲读指针: \t",
            "射频接收缓冲写指针: \t",
            "射频接收缓冲读指针: \t"
        };
		
        //// 协议栈内部的统计信息
        //private int statistic_mac_tx = 0;       // mac sent success
        //private int statistic_mac_rx = 0;		// received mac packets  不包括ACK
        //private int statistic_mac_drop = 0;		// drop due to mac buffer full
        //private int statistic_mac_err = 0;		// mac sent fail

        //private int statistic_data_rx = 0;      // received data for self node
        //private int statistic_data_tx = 0;		// sent data from self
        //private int statistic_data_drop = 0;    // data dropped due to send buffer full

        // 缓冲串口数据
        List<byte> receivedData = new List<byte>(128);
        // 串口统计数据
        private int statisticReceivedPacket = 0;
        private int statisticSendPacket = 0;
        private int statisticReceiveBytes = 0;
        private int statisticSendBytes = 0;

        // 窗体构造函数
        public MainForm()
        {
            InitializeComponent();
        }

        // 加载窗体，初始化各控件
        private void MainForm_Load(object sender, EventArgs e)
        {
            // 配置串口为：(COM1, 9600, 8, None, 1)
            // 默认选中 COM1
            this.comboBoxPortName.SelectedIndex = 0;
            // 波特率选中 9600
            this.comboBoxBaud.SelectedIndex = 5;
            // 数据位为 8
            this.comboBoxDataBits.SelectedIndex = 3;
            // 先获取系统的停止位
            this.comboBoxParity.DataSource = Enum.GetNames(typeof(Parity));
            // 然后设置停止位 None
            this.comboBoxParity.SelectedIndex = 0;
            // 先获取系统的停止位
            this.comboBoxStopBits.DataSource = Enum.GetNames(typeof(StopBits));
            // 然后设置停止位 1
            this.comboBoxStopBits.SelectedIndex = 1;
            // 设置上下文菜单事件
            for (int i = 0; i < this.contextMenuSetPower.Items.Count; i++ )
            {
                // 设置事件处理程序
                this.contextMenuSetPower.Items[i].Click += new EventHandler(toolStripMenuItemSetPower_Click);
            }

            // 将窗体的输入焦点转移到发送控件中
            this.richTextBoxSend.Focus();
        }

        // 自定义函数，打开串口
        private bool openSerialPort()
        {
            // 是否能够成功打开串口
            bool openSuccessed = true;

            // 尝试打开串口
            try
            {
                // 获取当前的串口配置情况
                this.serialPortMain.PortName = (string)comboBoxPortName.SelectedItem;
                this.serialPortMain.BaudRate = Convert.ToInt32(comboBoxBaud.SelectedItem);
                this.serialPortMain.DataBits = Int32.Parse((string)comboBoxDataBits.SelectedItem);
                this.serialPortMain.Parity = (Parity)Enum.Parse(typeof(Parity), comboBoxParity.Text);
                this.serialPortMain.StopBits = (StopBits)Enum.Parse(typeof(StopBits), comboBoxStopBits.Text);
                // 打开串口
                this.serialPortMain.Open();
            }
            catch (Exception ex)
            {
                // 设置串口状态
                openSuccessed = false;
                // 提示用户，串口出错
                MessageBox.Show(ex.Message + "请检查此串口是否被占用", "打开串口COM1出错");
            }
            // 返回串口打开状况
            return openSuccessed;
        }

        // 用户尝试打开/关闭串口
        private void buttonPortOper_Click(object sender, EventArgs e)
        {
            // 查看串口状态，判断用户操作——打开、关闭
            if (this.serialPortMain.IsOpen)
            {
                // 串口当前处于打开状态，关闭串口，所有的硬件操作都有可能出错
                // 首先，如果用户正在自动发送，则关掉定时器
                if(this.checkBoxAutoSend.Checked)
                {
                    // 用户正在自动发送，停止自动发送
                    autoSendCompleteHandle();
                }
                try
                {
                    this.serialPortMain.Close();
                }
                catch (Exception)
                {
                }
                // 更新图片
                this.buttonPortOper.BackgroundImage = global::UartAssistant.Properties.Resources.ClosedPort;
            }
            else
            {
                // 用户打开串口
                if (openSerialPort())
                {
                    // 如果成功打开，更新图片
                    this.buttonPortOper.BackgroundImage = global::UartAssistant.Properties.Resources.OpenedPort;
                }
            }
        }

        // 获得节点地址
        private void linkLabelGetAddress_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            // 发送命令，得到节点地址
            byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x04, FRAME_TYPE_GET_PARA, PARA_TYPE_MYSADDR, 0x00, 0x00, 0xF0 };

            // Ok,发送命令
            // 首先，查看串口打开状态
            if (this.serialPortMain.IsOpen)
            {
                try
                {
                    this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);

                    // 更新数据显示
                    setSendBytesLabel(addressCommand.Length);
					// 显示此命令的原始信息
					showFrameHex(addressCommand);
                }
                catch (Exception ex)
                {
                    // 发送数据失败
                    MessageBox.Show("获取地址失败，原因：" + ex.Message, "获取地址失败");
                }
            }
            else
            {
                // 提示用户打开串口
                MessageBox.Show("串口未打开，请您先打开串口!", "提示");
            }
        }

        // 获得节点的发送功率
        private void linkLabelGetPower_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            // 发送命令，得到节点地址
            byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x04, FRAME_TYPE_GET_PARA, PARA_TYPE_TXPOWER, 0x00, 0x00, 0xF1 };

            // Ok,发送命令
            // 首先，查看串口打开状态
            if (this.serialPortMain.IsOpen)
            {
                try
                {
                    this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);

                    // 更新数据显示
                    setSendBytesLabel(addressCommand.Length);
					// 显示此命令的原始信息
					showFrameHex(addressCommand);
                }
                catch (Exception ex)
                {
                    // 发送数据失败
                    MessageBox.Show("获取节点功率失败，原因：" + ex.Message, "获取地址失败");
                }
            }
            else
            {
                // 提示用户打开串口
                MessageBox.Show("串口未打开，请您先打开串口!", "提示");
            }
        }

        // 获得字符串的校验值
        private byte getDataCheckSum(List<byte> sendContent)
        {
            // 得到子串(长度＋要发送的数据)的检验值
            if (sendContent != null)
            {
                // 存在字符串中所有项的和值
                byte sum = 0;
                // 当前字符串长度
                int stringLength = sendContent.Count;
                // 得到和,不考虑前导码
                for (int i = 1; i < stringLength; i++)
                {
                    sum += sendContent[i];
                }

                // 返回校验值
                return (byte)(0xFF - sum);
            }

            return 0;
        }

        // 只发送用户的数据信息
        private void buttonSend_Click(object sender, EventArgs e)
        {
            // 根据用户的操作类型来封装发送/停止发送
			if (this.timerAutoSend.Enabled == true)
			{
				this.autoSendCompleteHandle();
				return;
			}
            // 首先，检查串口是否已经打开
            if (this.serialPortMain.IsOpen == false)
            {
                // 提示用户打开串口
                MessageBox.Show("当前串口处于关闭状态，请您先打开串口", "提示");
                return;
            }
            // 查看用户是否已经输入了数据
            if (this.richTextBoxSend.Text == string.Empty)
            {
                // 提示用户输入发送的地址
                MessageBox.Show("请输入您要发送的数据!", "提示");
                // 将焦点集中到地址输入框
                this.richTextBoxSend.Focus();
                return;
            }

			// 先将用户的数据装成帧
			if (checkBoxHexSend.CheckState == CheckState.Checked)
			{
				// 直接包装十六进制字符串
				autoSendFrame = this.packetHexToFrame(this.richTextBoxSend.Text);
				if (autoSendFrame == null)
				{
					MessageBox.Show("请检查您输入的十六进制数是否合法：[a-fA-F0-9]", "发送十六进制数据失败");
					return;
				}
			}
			else
			{
				autoSendFrame = this.packetDataToFrame(this.richTextBoxSend.Text);
			}

			// 查看用户是否点击了自动发送
			if (this.checkBoxAutoSend.CheckState == CheckState.Checked)
			{
				this.autoSendThread(autoSendFrame);
			}
			else
			{
				// 尝试一次发送数据
				this.loadDataToSerial(autoSendFrame);
			}
			// 显示命令原始信息(十六进制)
			showFrameHex(autoSendFrame.ToArray());
        }

		// 将十六进制的字符串转化为ASCII,每两位char为一个十六进制数
		private string ChangeHexToASCII(string hex)
		{
			// 首先、剔除空格
			hex = System.Text.RegularExpressions.Regex.Replace(hex, @"\s", "");
			string hexResult = "";
			string str = @"[a-fA-F0-9]";

			// 剔除非十六进制的字符
			foreach (char ch in hex)
			{
				if (System.Text.RegularExpressions.Regex.IsMatch(ch.ToString(), str))
				{
					hexResult += ch;
				}
				else
					continue;
			}

			hex = hexResult;
			int length = hex.Length;
			// 非空
			if (length == 0)
				return "";

			// 开始转化
			StringBuilder sb = new StringBuilder();
			int i;
			int tempN;
			char tempCh;

			for (i = 0; i <= hex.Length - 2; i += 2)
			{
				tempCh = (char)Int32.Parse(hex.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
				sb.Append(tempCh);
				//sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hex.Substring(i, 2), 
				//	System.Globalization.NumberStyles.HexNumber))));
			}			
			// 如果长度为奇数个，则最后一位单独拿出来转化
			if (length % 2 != 0)
			{
				tempN = (char)Int32.Parse(hex.Substring(i, 1), System.Globalization.NumberStyles.AllowHexSpecifier);
				tempCh = (char)tempN;
				sb.Append(tempCh);
			}

			return sb.ToString();
		}

		// 启动自动发送数据帧线程
		private void autoSendThread(List<byte> sendFrame)
		{
			// 设置禁用标志
			this.autoSendStartHandle();

			// 获取发送间隔和发送数据个数
			autoSendNumber = (int)this.numericUpDownSendNumber.Value;
			autoSendPeriod = (int)this.numericUpDownSendTime.Value;

			// Ok, 启动定时器
			this.timerAutoSend.Interval = autoSendPeriod;
			this.timerAutoSend.Start();
		}

		// 自动发送线程开始时，对窗体的控件进行初始化
		private void autoSendStartHandle()
		{
			// 禁用自动发送的控件直到自动发送结束
			this.numericUpDownSendNumber.Enabled = false;
			this.numericUpDownSendTime.Enabled = false;
			this.richTextBoxSend.Enabled = false;
			this.buttonSend.Text = "停止发送";

			this.currentSendNumber = 0;
		}
		
		// 定时器完成发送指定个数后的操作
		private void autoSendCompleteHandle()
		{
			// 停止自动发送
			this.timerAutoSend.Stop();
			// 启用发送内容
			this.richTextBoxSend.Enabled = true;

			// 启用改变发送频率和发送个数
			this.numericUpDownSendTime.Enabled = true;
			this.numericUpDownSendNumber.Enabled = true;
			this.buttonSend.Text = "发送";
			// 清空发送计数
			this.currentSendNumber = 0;
		}

		// 定时器每触发一次，发送一次数据
		private void timerAutoSend_Tick(object sender, EventArgs e)
		{
			// 发送指定数目次数据
			if (currentSendNumber < autoSendNumber)
			{
				this.loadDataToSerial(autoSendFrame);
				currentSendNumber++;
			}
			else
				this.autoSendCompleteHandle();
		}

		// 将用户输入的数据ASCII装成帧
		private List<byte> packetDataToFrame(string data)
		{
			// 装载发送字符串
            List<byte> sendContent = new List<byte>(16);
            // 前导码
            sendContent.Add((byte)FRAME_DELIMETER);
            // 封装发送数据的长度，包含Frame Data的1字节封包类型
			sendContent.Add((byte)(data.Length + 1));
            // 设置封装包的类型
            sendContent.Add((byte)FRAME_TYPE_TX_DATA);

            // 装包用户输入的内容
			sendContent.AddRange(System.Text.Encoding.Default.GetBytes(data));
            // 计算装包的校验值
            byte checkSum = getDataCheckSum(sendContent);
            // 装入校验值
            sendContent.Add(checkSum);

			return sendContent;
		}

		// 将用户输入的数据Hex装成帧
		private List<byte> packetHexToFrame(string hex)
		{
			// 装载发送字符串
			List<byte> sendContent = new List<byte>(16);
			// 前导码
			sendContent.Add((byte)FRAME_DELIMETER);

			// 首先、剔除空格
			hex = System.Text.RegularExpressions.Regex.Replace(hex, @"\s", "");
			string hexResult = "";
			string str = @"[a-fA-F0-9]";

			// 剔除非十六进制的字符
			foreach (char ch in hex)
			{
				if (System.Text.RegularExpressions.Regex.IsMatch(ch.ToString(), str))
				{
					hexResult += ch;
				}
				else
					continue;
			}

			hex = hexResult;
			int length = hex.Length;
			// 非空
			if (length == 0)
				return null;

			// 开始转化
			StringBuilder sb = new StringBuilder();
			int i;
			byte tempN;
			// 封装发送数据的长度，包含Frame Data的1字节封包类型
			if (length % 2 != 0)
				sendContent.Add((byte)((length+1) / 2 + 1));
			else
				sendContent.Add((byte)(length / 2 + 1));

			// 设置封装包的类型
			sendContent.Add((byte)FRAME_TYPE_TX_DATA);

			for (i = 0; i <= length - 2; i += 2)
			{
				tempN = (byte)Int32.Parse(hex.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
				sendContent.Add(tempN);
			}
			// 如果长度为奇数个，则最后一位单独拿出来转化
			if (length % 2 != 0)
			{
				tempN = (byte)Int32.Parse(hex.Substring(i, 1), System.Globalization.NumberStyles.AllowHexSpecifier);
				sendContent.Add(tempN);
			}

			// 计算装包的校验值
			byte checkSum = getDataCheckSum(sendContent);
			// 装入校验值
			sendContent.Add(checkSum);

			return sendContent;
		}

        // 检查完毕，装载用户的数据
        private void loadDataToSerial(List<byte> sendFrame)
        {
            try
            {
                this.serialPortMain.Write(sendFrame.ToArray(), 0, sendFrame.Count);

				// 更新发送的数据包个数
                this.statisticSendPacket++;
                this.labelSendPacketNum.Text = "发送数据帧：" + this.statisticSendPacket.ToString();
                // 更新发送的字节数
                setSendBytesLabel(sendFrame.Count);
            }
            catch (Exception ex)
            {
                MessageBox.Show("发送数据错误，原因：" + ex.Message, "未成功发送数据");
            }
        }

        // 清空数据包计数器
        private void linkLabelClearCount_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            // 将数据包计数置为初始状态
            // 接收包数
            this.statisticReceivedPacket = 0;
            this.labelRecvPacketNum.Text = "接收数据帧：" + this.statisticReceivedPacket.ToString();
            // 发送包数
            this.statisticSendPacket = 0;
			this.labelSendPacketNum.Text = "发送数据帧：" + this.statisticSendPacket.ToString(); ;
            // 接收字节数
            this.statisticReceiveBytes = 0;
            this.labelRecvBytes.Text = "接收字节：" + this.statisticReceiveBytes.ToString();
            // 发送字节数
            this.statisticSendBytes = 0;
            this.labelSendBytes.Text = "发送字节：" + this.statisticSendBytes.ToString(); ;
        }

		// 自定义事件响应函数，用于菜单项“设置功率等级...”的操作
		private void toolStripMenuItemSetPower_Click(object sender, EventArgs e)
		{
			// 
			// 保存发生了点击事件的那个菜单项
			ToolStripMenuItem tempItem = null;
			// 得到菜单项的索引值
			int activedMenuIndex = -1;

			// 尝试转换类型
			try
			{
				// 强制类型转换
				tempItem = (ToolStripMenuItem)sender;
				// 获得下标值，用于选择设置功率值
				activedMenuIndex = this.contextMenuSetPower.Items.IndexOf(tempItem);
				// 设置功率，发送的字符串
				byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x04, FRAME_TYPE_SET_PARA, PARA_TYPE_TXPOWER, 0x00, 0x00, 0x00 };
				// 写入字符串
				addressCommand[4] = RF_power[activedMenuIndex];

				// 得到校验值
				addressCommand[6] = getDataCheckSum(new List<byte>(addressCommand));

				// 发送数据
				this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);
				// 更新数据显示
				setSendBytesLabel(addressCommand.Length);
				// 显示此命令的原始信息
				showFrameHex(addressCommand);
			}
			catch (Exception ex)
			{
				// 调试用，运行的时候删除对话框
				MessageBox.Show("设置功率等级出错，原因：" + ex.Message, "设置功率出错", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
				return;
			}
		}

		// 获得RSSI的值
		private void linkLabelRSSI_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			// 发送命令，得到节点的最近RSSI值
			byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x04, FRAME_TYPE_GET_PARA, PARA_TYPE_RSSI, 0x00, 0x00, 0xEF };

			// Ok,发送命令
			// 首先，查看串口打开状态
			if (this.serialPortMain.IsOpen)
			{
				try
				{
					this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);

					// 更新数据显示
					setSendBytesLabel(addressCommand.Length);
					// 显示此命令的原始信息
					showFrameHex(addressCommand);
				}
				catch (Exception ex)
				{
					// 发送数据失败
					MessageBox.Show("获取节点RSSI值失败，原因：" + ex.Message, "获取RSSI失败");
				}
			}
			else
			{
				// 提示用户打开串口
				MessageBox.Show("串口未打开，请您先打开串口!", "提示");
			}
		}

		// 清空接收区或输入区中的文字
		private void toolStripMenuItemClearAll_Click(object sender, EventArgs e)
		{
			// 清空RichTextBox中的文字
			try
			{
				if (this.richTextBoxReceived.RectangleToScreen(this.richTextBoxReceived.ClientRectangle).Contains(this.contextMenuStripTextOperation.PointToScreen(new Point(0, 0))))
				{
					this.richTextBoxReceived.Clear();
					this.richTextBoxReceived.Focus();
				}
				else
				{
					this.richTextBoxSend.Clear();
					this.richTextBoxSend.Focus();
				}
			}
			catch (System.Exception ex)
			{
				MessageBox.Show("清除文字错误，原因：" + ex.Message);
			}
		}

		// 查看协议栈中的统计信息
		private void linkLabelGetStatistics_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			// 发送命令，得到协议栈中的统计信息
			byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x01, FRAME_TYPE_GET_STATISTIC, 0x8E };

			// Ok,发送命令
			// 首先，查看串口打开状态
			if (this.serialPortMain.IsOpen)
			{
				try
				{
					this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);

					// 更新数据显示
					setSendBytesLabel(addressCommand.Length);
					// 显示此命令的原始信息
					showFrameHex(addressCommand);
				}
				catch (Exception ex)
				{
					// 发送数据失败
					MessageBox.Show("获取协议栈中的统计信息，原因：" + ex.Message, "获取统计信息失败");
				}
			}
			else
			{
				// 提示用户打开串口
				MessageBox.Show("串口未打开，请您先打开串口!", "提示");
			}
		}

		// 清空协议栈统计信息
		private void linkLabelClrStatistic_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			// 发送命令，得到协议栈中的统计信息
			byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x01, FRAME_TYPE_CLR_STATISTIC, 0x8C };

			// Ok,发送命令
			// 首先，查看串口打开状态
			if (this.serialPortMain.IsOpen)
			{
				try
				{
					this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);

					// 更新数据显示
					setSendBytesLabel(addressCommand.Length);
					// 显示此命令的原始信息
					showFrameHex(addressCommand);
				}
				catch (Exception ex)
				{
					// 发送数据失败
					MessageBox.Show("失败：清空协议栈中的统计信息，原因：" + ex.Message, "清空统计信息失败");
				}
			}
			else
			{
				// 提示用户打开串口
				MessageBox.Show("串口未打开，请您先打开串口!", "提示");
			}
		}

		// 显示设置功率菜单
		private void buttonSetRFPower_MouseUp(object sender, MouseEventArgs e)
		{
			// 设置节点功率，在鼠标位置显示菜单
			// 首先，串口必须在打开状态
			if (this.serialPortMain.IsOpen == false)
			{
				// 提示用户打开串口
				MessageBox.Show("当前串口处于关闭状态，请您先打开串口", "提示");
				return;
			}
			// 然后，确定菜单显示的位置
			Point showPoint = new Point(e.X + this.buttonSetRFPower.Parent.Location.X + this.buttonSetRFPower.Location.X, e.Y + this.buttonSetRFPower.Parent.Location.Y + this.buttonSetRFPower.Location.Y);
			// 在指定的位置显示菜单
			this.contextMenuSetPower.Show(this.PointToScreen(showPoint));
		}

		// 只停止自动发送，无它
		private void checkBoxAutoSend_CheckedChanged(object sender, EventArgs e)
		{
			// 定时器是否正在运行
			if (this.timerAutoSend.Enabled == true)
			{
				this.autoSendCompleteHandle();
			}
		}

		/**************************************************************************
		 * 代理：
		 * 接收线程对窗体控件的操作
		 *************************************************************************/
        // 代理，将串口获取的字符串显示到控件线程的接收区内
        delegate void SetTextCallback(string text);
        delegate void SetLabelRecvBytesCallback(int receivedBytes);
        delegate void SetLabelSendBytesCallback(int sendBytes);
        delegate void SetLabelAddressCallback(string address);
        delegate void SetLabelPowerCallback(string power);
        delegate void SetLabelRSSICallback(string rssi);
		delegate void SetLabelVersionCallback(string version);

        // 设置字符串的实际函数
        private void setReceivedText(string text)
        {
            try
            {
                // 指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，
                // 因为调用方位于创建控件所在的线程以外的线程中。
                if (this.richTextBoxReceived.InvokeRequired)
                {
                    // 如果需要调用Invoke的话
                    SetTextCallback delegateChangeText = new SetTextCallback(setReceivedText);
                    this.Invoke(delegateChangeText, new object[] { text });
                }
                else
                {
					this.richTextBoxReceived.AppendText(text);
					// 移动滚动条到最下面
					this.richTextBoxReceived.SelectionStart = this.richTextBoxReceived.Text.Length;
					this.richTextBoxReceived.ScrollToCaret();

                    this.labelRecvPacketNum.Text = "接收数据帧：" + this.statisticReceivedPacket.ToString();
                }
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

        // 设置地址显示
        private void setLabelAddress(string text)
        {
            try
            {
                // 指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，
                // 因为调用方位于创建控件所在的线程以外的线程中。
                if (this.labelAddressDisplay.InvokeRequired)
                {
                    // 如果需要调用Invoke的话
                    SetLabelAddressCallback delegateChangeAddress = new SetLabelAddressCallback(setLabelAddress);
                    this.Invoke(delegateChangeAddress, new object[] { text });
                }
                else
                {
                    this.labelAddressDisplay.Text = text;
                }
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

        // 设置功率显示
        private void setLabelPower(string text)
        {
            try
            {
                // 指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，
                // 因为调用方位于创建控件所在的线程以外的线程中。
                if (this.labelPowerDisplay.InvokeRequired)
                {
                    // 如果需要调用Invoke的话
                    SetLabelPowerCallback delegateChangePower = new SetLabelPowerCallback(setLabelPower);
                    this.Invoke(delegateChangePower, new object[] { text });
                }
                else
                {
                    this.labelPowerDisplay.Text = text;
                }
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

        // 设置RSSI显示
        private void setLabelRSSI(string text)
        {
            try
            {
                // 指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，
                // 因为调用方位于创建控件所在的线程以外的线程中。
                if (this.labelDisplayRSSI.InvokeRequired)
                {
                    // 如果需要调用Invoke的话
                    SetLabelRSSICallback delegateChangePower = new SetLabelRSSICallback(setLabelRSSI);
                    this.Invoke(delegateChangePower, new object[] { text });
                }
                else
                {
                    this.labelDisplayRSSI.Text = text;
                }
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

		// 设置Version显示
		private void setLabelVersion(string text)
		{
			try
			{
				// 指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，
				// 因为调用方位于创建控件所在的线程以外的线程中。
				if (this.labelVersionNo.InvokeRequired)
				{
					// 如果需要调用Invoke的话
					SetLabelRSSICallback delegateChangePower = new SetLabelRSSICallback(setLabelVersion);
					this.Invoke(delegateChangePower, new object[] { text });
				}
				else
				{
					this.labelVersionNo.Text = text;
				}
			}
			catch (Exception)
			{
				//MessageBox.Show(ex.Message);
			}
		}

        // 设置接收到的字符显示
        private void setReceivedBytesLabel(int receivedBytes)
        {
            try
            {
                // 指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，
                // 因为调用方位于创建控件所在的线程以外的线程中。
                if (this.labelRecvBytes.InvokeRequired)
                {
                    // 如果需要调用Invoke的话
                    SetLabelRecvBytesCallback delegateChangeLabel = new SetLabelRecvBytesCallback(setReceivedBytesLabel);
                    this.Invoke(delegateChangeLabel, new object[] { receivedBytes });
                }
                else
                {
                    // 不需要，在本线程中
                    this.statisticReceiveBytes += receivedBytes;
                    this.labelRecvBytes.Text = "接收字节：" + this.statisticReceiveBytes.ToString();
                }
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

        // 设置发送的字符显示
        private void setSendBytesLabel(int sendBytes)
        {
            try
            {
                // 指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，
                // 因为调用方位于创建控件所在的线程以外的线程中。
                if (this.labelSendBytes.InvokeRequired)
                {
                    // 如果需要调用Invoke的话
                    SetLabelSendBytesCallback delegateChangeLabel = new SetLabelSendBytesCallback(setReceivedBytesLabel);
                    this.Invoke(delegateChangeLabel, new object[] { sendBytes });
                }
                else
                {
                    // 不需要，在本线程中
                    this.statisticSendBytes += sendBytes;
                    this.labelSendBytes.Text = "发送字节：" + this.statisticSendBytes.ToString();
                }
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

		/**************************************************************************
		 * 接收线程相应操作
		 *************************************************************************/
        // 接收串口数据的后台线程
        private void backgroundWorkerReceive_DoWork(object sender, DoWorkEventArgs e)
        {
            // 获得后台操作对象
            BackgroundWorker worker = sender as BackgroundWorker;

            // 处理，查看返回
            e.Result = analyseReceivedData(worker, e);
        }

        // 自定义函数，分析刚刚接收到的串口数据
        private bool analyseReceivedData(BackgroundWorker worker, DoWorkEventArgs e)
        {
            // 读取串口数据
            // Ok,处理事件，将任务都交给后台线程运行
            byte[] receivedBytes;
            // 读取串口数据
            // 首先，获得数据长度
            int bytesToRead = this.serialPortMain.BytesToRead;
            receivedBytes = new byte[bytesToRead];
            // 读取数据
            if (this.serialPortMain.Read(receivedBytes, 0, bytesToRead) <= 0)
            {
                // 如果没有数据，直接返回
                e.Cancel = true;
                return true;
            }
            setReceivedBytesLabel(receivedBytes.Length);
            // 将新的数据加入缓冲区
            receivedData.AddRange(receivedBytes);

			/*******************************************************
			 * 上面已经从串口中读取了最新的数据，可能数据包不完整，下面对
			 * 读取的数据包进行分析
			 *******************************************************/

            // TODO: 同步前导码，当前不考虑数据可能不完整的情况
            while (receivedData.Count > 0)
            {
                // 查看前导码
                while (receivedData[0] != FRAME_DELIMETER && receivedData.Count != 0) 
                {
                    // 删除没有前导信息的数据
                    this.receivedData.RemoveAt(0);
                }
                // 查看是否有需要的前导码,必须能读取到长度值且数据包够长度
                if (receivedData.Count < 2 || receivedData[1] + 3 > receivedData.Count)
                {
                    return true;
                }
				
                // Ok,有前导码，且长度符合要求，记录此数据包的长度
                int currentPacketLength = receivedData[1]+3;
#if DEBUG
				// 输出原始数据
				// 分析串口数据类型
				string tempUnknowString = string.Empty;
				for (int i = 0; i < currentPacketLength; i++)
				{
					tempUnknowString += receivedData[i].ToString("X2") + " ";
				}
				setReceivedText("接收到的原始数据：" + tempUnknowString + "\n");
#endif

                // 找到前导码，查看校验值
                if (verifyCheckSum(receivedData, 0, currentPacketLength) == false)
                {
                    // 校验不正确通过，读取下一个前导码
                    this.receivedData.RemoveAt(0);
                    continue; 
                }

                // 保存分析数据的显示文本
                string parsedString = string.Empty;
                try
                {
                    // 看类型
                    switch (receivedData[2])
                    {
                        case FRAME_TYPE_RX_DATA:
                            // 读取数据，不包含校验位
                            for (int i = 3; i < currentPacketLength - 1; i++)
                            {
                                parsedString += (char)receivedData[i];
                            }
                            // Ok，接收到数据，更新文本
                            this.statisticReceivedPacket++;
                            setReceivedText("接收到广播数据：" + parsedString + "\n");
                            break;
                        case FRAME_TYPE_RET_PARA:
                            switch (receivedData[3])
                            {
                                case PARA_TYPE_TXPOWER:
                                    parsedString = "0x" + receivedData[4].ToString("X2");
                                    setLabelPower(parsedString);
                                    break;
                                case PARA_TYPE_MYSADDR:
                                    parsedString = "0x" + receivedData[5].ToString("X2");
                                    parsedString += receivedData[4].ToString("X2");
                                    setLabelAddress(parsedString);
                                    break;
                                case PARA_TYPE_RSSI:
                                    parsedString = "0x" + receivedData[4].ToString("X2");
                                    setLabelRSSI(parsedString);
									break;
								case PARA_TYPE_VERSION:
                                    parsedString = "V" + receivedData[5].ToString();
									parsedString += ".";
                                    parsedString += receivedData[4].ToString();
									setLabelVersion(parsedString);
                                    break;
                            }
                            break;
                        case FRAME_TYPE_RET_STATISTIC:
                            // 数据格式如下：
                            // 0x7E, 0x0F, 0x71, 
                            // 0x22, 0x00(MAC发送成功包), 
                            // 0x04, 0x00(MAC层接收包), 
                            // 0x00, 0x00(MAC Buff Full而放弃的包), 
                            // 0x00, 0x00(MAC发送错误),
                            // 0x00, 0x00(received data for self node), 
                            // 0x1E, 0x00(sent data from self), 
                            // 0x00, 0x00(data dropped due to send buffer full), 
                            // 0x3B

                            // 读取数据，不包含校验位
                            for (int i = 3; i < currentPacketLength - 1; i+=2)
                            {
								parsedString += satisticsInformation[(i - 3) >> 1];
								parsedString += "0x" + receivedData[i+1].ToString("X2");
								parsedString += receivedData[i].ToString("X2");
								parsedString += "\n";
                            }
							setReceivedText(parsedString);
                            break;
						case PAPRA_TYPE_RET_CPSR:
							for (int i = 3; i < currentPacketLength - 1; i += 2)
							{
								parsedString += CPSRInformation[(i - 3) >> 1];
								parsedString += "0x" + receivedData[i + 1].ToString("X2");
								parsedString += receivedData[i].ToString("X2");
								parsedString += "\n";
							}
							setReceivedText(parsedString);
							break;
                        default:
                            string unknowString = string.Empty;
                            for (int i = 0; i < currentPacketLength; i++)
                            {
                                unknowString += receivedData[i].ToString("X2") + " ";
                            }
                            setReceivedText("未知类型数据：" + unknowString + "\n");
                            break;
                    }
                }
                catch (System.Exception)
                {
                }
                // 指针前移
                this.receivedData.RemoveRange(0, currentPacketLength);
            }
            return true;
        }

        // 验证字符串的校验
        private bool verifyCheckSum(List<byte> orignalString, int index, int length)
        {
            // index为前导码的下标位置
            // 校验接收到的字符串
            byte sum = 0;

            // 得到字符串的和,不考虑前导码
            for (int i = index+1; i < length; i++)
            {
                sum += orignalString[i];
            }

            // 查看校验是否正确
            if (sum == 0xFF)
            {
                return true;
            }

            return false;
        }

        // 从串口到来数据时引发此事件
        private void serialPortMain_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // 查看数据处理线程是否工作完
            if (this.backgroundWorkerReceive.IsBusy == false)
            {
                // 启动线程处理
                this.backgroundWorkerReceive.RunWorkerAsync();
            }
        }

        // 重新启动自身
        private void backgroundWorkerReceive_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // 查看串口是否有新的数据
            if (this.serialPortMain.BytesToRead > 0 && this.backgroundWorkerReceive.IsBusy == false)
            {
                // 有数据，Ok，启动处理数据线程
                BackgroundWorker work = sender as BackgroundWorker;
                work.RunWorkerAsync();
            }
        }

		/************************************************************
		 * Debug
		 ***********************************************************/
		// 将整个命令显示为十六进制
		private void showFrameHex(byte[] frame)
		{
			// 输出原始数据
			// 分析串口数据类型
			string tempFrameString = string.Empty;
			for (int i = 0; i < frame.Length; i++)
			{
				tempFrameString += frame[i].ToString("X2") + " ";
			}
			this.labelSendFrameShow.Text = "命令原始数据：" + tempFrameString;
		}

		// 转化成十六进制或转化为ASCII显示
		private void checkBoxHexSend_CheckedChanged(object sender, EventArgs e)
		{
			// 非空
			if (this.richTextBoxSend.Text == String.Empty || this.richTextBoxSend.Text == "")
				return;
			// 转化
			if (this.checkBoxHexSend.CheckState == CheckState.Checked)
			{
				this.richTextBoxSend.Text = this.ChangeASCIIToHex(this.richTextBoxSend.Text);
			}
			else
			{
				string tempStr = this.ChangeHexToASCII(this.richTextBoxSend.Text);
				if (tempStr == string.Empty)
				{
					MessageBox.Show("检测到您输入的十六进制字符[a-fA-F0-9]中包含非法字符，请仔细检查!", "提示");
				}
				this.richTextBoxSend.Text = tempStr;
			}
		}

		// 将ASCII的字符串转化为Hex字符串
		private string ChangeASCIIToHex(string ascii)
		{
			string hex = "";
			foreach (char c in ascii)
			{
				int tmp = c;
				hex += String.Format("{0:X2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
				hex += " ";
			}
			hex = hex.Remove(hex.Length-1);
			return hex;
		}

		// 如果十六进制，则检查用户输入
		private void richTextBoxSend_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (this.checkBoxHexSend.CheckState == CheckState.Checked)
			{
				// 首先，判断空格
				if (e.KeyChar == (char)Keys.Space)
					return;
				string str = @"[a-fA-F0-9]";
				e.Handled = !System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), str);
			}
		}

		// 查看协议栈版本号
		private void linkLabelVersion_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			// 发送命令，得到节点的最近RSSI值
			byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x04, FRAME_TYPE_GET_PARA, PARA_TYPE_VERSION, 0x00, 0x00, 0xE2 };

			// Ok,发送命令
			// 首先，查看串口打开状态
			if (this.serialPortMain.IsOpen)
			{
				try
				{
					this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);

					// 更新数据显示
					setSendBytesLabel(addressCommand.Length);
					// 显示此命令的原始信息
					showFrameHex(addressCommand);
				}
				catch (Exception ex)
				{
					// 发送数据失败
					MessageBox.Show("获取节点协议栈版本号失败，原因：" + ex.Message, "获取协议栈版本号失败");
				}
			}
			else
			{
				// 提示用户打开串口
				MessageBox.Show("串口未打开，请您先打开串口!", "提示");
			}
		}

		// 查看协议栈当前的CPSR值
		private void linkLabelGetCPSR_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			// 发送命令，得到协议栈中的统计信息
			byte[] addressCommand = new byte[] { FRAME_DELIMETER, 0x01, PAPRA_TYPE_GET_CPSR, 0x8B };

			// Ok,发送命令
			// 首先，查看串口打开状态
			if (this.serialPortMain.IsOpen)
			{
				try
				{
					this.serialPortMain.Write(addressCommand, 0, addressCommand.Length);

					// 更新数据显示
					setSendBytesLabel(addressCommand.Length);
					// 显示此命令的原始信息
					showFrameHex(addressCommand);
				}
				catch (Exception ex)
				{
					// 发送数据失败
					MessageBox.Show("获取协议栈中的CPSR信息，原因：" + ex.Message, "获取CPSR信息失败");
				}
			}
			else
			{
				// 提示用户打开串口
				MessageBox.Show("串口未打开，请您先打开串口!", "提示");
			}
		}
    }
}

